package com.enfi.api.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.enfi.api.util.SecUser;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.immortal.httpclientutil.HttpClientUtil;
import com.immortal.httpclientutil.common.HttpConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.*;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class ImAuthenticationFilter extends GenericFilterBean implements ApplicationEventPublisherAware {
    private static final Log logger = LogFactory.getLog(ImAuthenticationFilter.class);
    private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
    private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler("/login?error");
    private ApplicationEventPublisher eventPublisher;
    private AuthenticationManager authenticationManager;
    private String url;
    private String key;
    private List<String> allow = new ArrayList<>();
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    public ImAuthenticationFilter(String url, List<String> allow) {
        this.key = "jeesite.session.id";
        this.url = url;
        this.allow = allow;
    }

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        boolean debug = logger.isDebugEnabled();
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        for (String s : allow) {
            if (antPathMatcher.match(s, request.getRequestURI())) {
                chain.doFilter(request, response);
                return;
            }
        }
        if (SecurityContextHolder.getContext().getAuthentication() == null || StringUtils.hasText(request.getParameter("refresh"))) {
            Authentication rememberMeAuth=null;
            try {
                rememberMeAuth= extract(request);
                if (rememberMeAuth != null) {

                    SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);
                    this.onSuccessfulAuthentication(request, response, rememberMeAuth);
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("SecurityContextHolder populated with remember-me token: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
                    }
                }
            } catch (AuthenticationException var8) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("SecurityContextHolder not populated with remember-me token, as AuthenticationManager rejected Authentication returned by RememberMeServices: '" + rememberMeAuth + "'; invalidating remember-me token", var8);
                }
                this.onUnsuccessfulAuthentication(request, response, var8);
                return;
            }
            chain.doFilter(request, response);
        } else {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("SecurityContextHolder not populated with remember-me token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
            }

            chain.doFilter(request, response);
        }
    }

    public Authentication extract(HttpServletRequest request) {
        String tokenValue = "";
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equalsIgnoreCase(this.key)) {
                    tokenValue = cookie.getValue();
                    break;
                }
            }
        }
        if (!StringUtils.isEmpty(tokenValue)) {
            HttpConfig httpConfig = HttpConfig.custom().url(url + "?__sid=" + tokenValue);
            try {
                JSONObject jsonObject = JSON.parseObject(HttpClientUtil.get(httpConfig));
                String username = "";
                String userId = "";
                String numberNo = "";
                boolean isEnabled = false;
                if (jsonObject.containsKey("user")) {
                    JSONObject user = jsonObject.getJSONObject("user");
                    username = user.getString("userName");
                    userId = user.getString("userCode");
                    numberNo = user.getString("refCode");
                    String status = user.getString("status");
                    if ("0".equalsIgnoreCase(status)) {
                        isEnabled = true;
                    }
                }
                Set<GrantedAuthority> grantedAuthorities = Sets.newHashSet();
                if (jsonObject.containsKey("roleList")) {
                    JSONArray jsonArray = jsonObject.getJSONArray("roleList");
                    jsonArray.stream().forEach(v -> {
                        JSONObject tt = (JSONObject) v;
                        String id = tt.getString("id");
                        grantedAuthorities.add(new SimpleGrantedAuthority(id));
                    });
                }
                SecUser user = new SecUser(userId, username, "", grantedAuthorities, true, true, true, isEnabled);
                user.setData(Maps.newHashMap());
                user.getData().put("numberNo", numberNo);
                RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key,
                        user,
                        user.getAuthorities());
                auth.setDetails(new WebAuthenticationDetails(request));
                return auth;
            } catch (Exception e) {
                throw new BadCredentialsException("单点登陆获取登陆失败");
            }
        } else {
            return null;
        }
    }

    private boolean isAuthenticated() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication != null && !(authentication instanceof AnonymousAuthenticationToken);
    }

    public void destroy() {
    }

    protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
    }

    protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)throws IOException, ServletException{
        this.failureHandler.onAuthenticationFailure(request,response,failed);
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {
        Assert.notNull(successHandler, "successHandler cannot be null");
        this.successHandler = successHandler;
    }

    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }
}